package spring.ioc;

import com.sun.xml.internal.ws.util.StreamUtils;
import com.sun.xml.internal.ws.util.StringUtils;
import spring.annotation.Component;
import spring.annotation.ComponentScan;

import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author cino
 * justCode
 * SpringApplicationContext类的作用类似Spring原生ioc容器
 */
public class SpringApplicationContext {
    private Class configClass;
    private final ConcurrentHashMap<String, Object> ioc =
            new ConcurrentHashMap<>();

    // 构造器
    public SpringApplicationContext(Class configClass) {
        this.configClass = configClass;
        // System.out.println("this.configClass=" + this.configClass);
        // 获取要扫描的包
        // 1.先得到SpringConfig配置的@ComponentScan(value="spring.annotation.Component")
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        // 2.通过componentScan的value=> 即要扫描的包
        String path = componentScan.value();
        System.out.println("(path)要扫描的包= " + path);

        // 得到要扫描的包下的所有资源(类.class)
        // 1.得到类的加载器
        ClassLoader classLoader =
                SpringApplicationContext.class.getClassLoader();
        // 2.通过类的加载器获取到要扫描包的资源url=> 类似一个路径
        path = path.replace(".", "/");// 一定要把. 替换成 /
        URL resource =
                classLoader.getResource(path);
        System.out.println("resource=" + resource);
        // 3.将要加载的资源(.class)路径下的文件进行遍历=>io
        // 目录和文件夹本身就是一种特殊的文件
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                System.out.println("========");
                System.out.println(f.getAbsolutePath());
                // 获取到spring.component.MonsterDao
                String fileAbsolutePath = f.getAbsolutePath();

                // 这里我们只处理.class文件
                if (fileAbsolutePath.endsWith(".class")) {

                    // 1.获取到类名
                    String className =
                            fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
                    // System.out.println("className=" + className);
                    // 2.获取类的完整的路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;

                    //3.判断该类是不是需要注入容器,就看该类是不是有注解@Component @Service
                    try {
                        // aClass.isAnnotationPresent(Component.class)判断该类是否有Component注解
                        Class<?> aClass = classLoader.loadClass(classFullName);
                        if (aClass.isAnnotationPresent(Component.class))
                        //  || aClass.isAnnotationPresent(Controller.class) ||
                        // aClass.isAnnotationPresent(Service.class) ||
                        // aClass.isAnnotationPresent(Repository.class))
                        {
                            //获取到该注解
                            Component component = aClass.getDeclaredAnnotation(Component.class);
                            String value = component.value();
                            if (!"".endsWith(value)){
                                className = value;//替换
                            }
                            //这时就可以反射对象，并放入到容器中
                            Class<?> clazz = Class.forName(classFullName);
                            Object instance = clazz.newInstance();
                            //放入到容器中,将类名的首字母小写作为id
                            ioc.put(StringUtils.decapitalize(className), instance);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
